/*
  TextFinder.cpp - a simple text parser for Arduino data streams
  Copyright (c) 2009 Michael Margolis.  All right reserved.
  
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.
 
  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.
 
  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
#include "TextFinder.h"

#define NO_SKIP_CHAR  1  // a magic char not found in a valid numeric field

// private function to read characters from stream
// the constructors allow one and only one of the streams to be initialized
  char TextFinder::read()
  {   
    startMillis = millis();  
    if (clientStream != NULL)
    {     
      while(millis() < (startMillis + timeout)) 
      {
        if (clientStream->available() > 0)
          return clientStream->read();           
      }
    }
    else // serialStream must be valid
    {
      while(millis() < (startMillis + timeout)) 
      {
        if (serialStream->available() > 0)
          return serialStream->read();                   
      }   
    } 
    return 0;     // 0 indicates timeout     
  }

  // constructors 
  //default timeout is 5 seconds
  
  //Ethernet
  TextFinder::TextFinder(Client &stream, int timeout) :
                  clientStream(&stream), serialStream(NULL) 
  { 
    this->timeout = timeout * 1000L;     
  }

  //Serial
  TextFinder::TextFinder(HardwareSerial &stream, int timeout) : 
                           clientStream(NULL), serialStream(&stream)
  {
    this->timeout = timeout * 1000;     
  }
  
  // public methods
  //
  // find returns true if the target string is found
  boolean  TextFinder::find(char *target)
  {
    return findUntil(target, NULL);
  }
 
  // as above but search ends if the terminate string is found
  boolean  TextFinder::findUntil(char *target, char *terminate)
  {
    byte targetLen = strlen(target); 
    byte index = 0;  // maximum target string length is 255 bytes
    byte termIndex = 0;
    byte termLen = (terminate == NULL ? 0 : strlen(terminate));
    char c;
 
    if( *target == 0)
       return true;   // return true if target is a null string 
    while( (c = read()) != 0){   
      if( c == target[index]){        
        if(++index >= targetLen){ // return true if all chars in the target match
          return true;    
        }
      }
      else{        
        index = 0;  // reset index if any char does not match   
      }
      if(termLen > 0 && c == terminate[termIndex]){
         if(++termIndex >= termLen)  
           return false;       // return false if terminate string found before target string
      }
      else
          termIndex = 0;
    }
    return false;
  }

    // places the string between the prestring  and poststring in the given buffer
    // buffer must be one more than the longest string to get
    // the string will be truncated to fit the buffer length
    // end of string determined by a single character match to the first char of poststring
    // returns the number of characters placed in the buffer (0 means no valid data found)
  int TextFinder::getString( char *pre_string, char *post_string, char *buffer, int length)
  {  
    if( find(pre_string) ){ 
      int index = 0;   
      *buffer = 0;
      while(index < length-1 ){
        char c = read();
        if( c == 0 ){
          return 0;   // timeout returns 0 !
        }
        else if( c == *post_string ){
          buffer[index] = 0; // terminate the string
          return index;               // data got successfully
        }
        else{
          buffer[index++] = c;   
        }
      }
      buffer[index] = 0;
      return index; // Note: buffer full before the closing post_string encountered
    }   
    return 0;    //failed to find the prestring
  } 

  // getValue method:
  // returns the first valid (long) integer value from the current position.
  // initial characters that are not digits (or the minus sign) are skipped
  // function is terminated by the first character that is not a digit.
  long TextFinder::getValue()
  {
      return getValue(NO_SKIP_CHAR); // terminate on first non-digit character 
  }
  
  // as above but a given skipChar is ignored
  // this allows format characters (typically commas) in values to be ignored
  long TextFinder::getValue(char skipChar)
  {
      boolean isNegative = false;
      long value = 0;   
      char c;

      while( (c = read()) != '-' && (c < '0' || c > '9') )
         ;// ignore non numeric leading characters  
      do{ 
        if(c == skipChar)
           ; // ignore this charactor        
        else if(c == '-') 
           isNegative = true;          
        else if(c >= '0' && c <= '9')        // is c a digit?  
           value = value * 10 + c - '0';         
        c = read();        
      }
      while( (c >= '0' && c <= '9')  || c == skipChar );
      if(isNegative)
        value = -value;   
     return value;
  }

  // float version of getValue method:
  // as integer version but returns a floating point value
    float TextFinder::getFloat()
    {
       getFloat(NO_SKIP_CHAR);
    }

  // as above but the given skipChar is ignored
  // this allows format characters (typically commas) in values to be ignored  
  float TextFinder::getFloat(char skipChar){
      boolean isNegative = false;
      boolean isFraction = false;
      long value = 0;  
      float fValue;       
      char c;
      float fraction = 1.0;

      while( (c = read()) != '-' && (c < '0' || c > '9') )
         ; // ignore non numeric leading characters
      do{    
        if(c == skipChar)
           ; // ignore 
        else if(c == '-') 
           isNegative = true;     
        else if (c == '.')
            isFraction = true;
        else if(c >= '0' && c <= '9')  {      // is c a digit?  
           value = value * 10 + c - '0'; 
           if(isFraction)
              fraction *= 0.1;
        }           
        c = read();     
      }
      while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar );
      if(isNegative)
        value = -value;      
      if(isFraction)
        return value * fraction;        
      else  
        return value;
  }
